package com.ta.gulimall.product.service.impl;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ta.gulimall.common.constant.ProductConstant;
import com.ta.gulimall.common.constant.RedisConstant;
import com.ta.gulimall.common.utils.system.PageUtils;
import com.ta.gulimall.common.utils.system.Query;
import com.ta.gulimall.common.utils.RedisUtil;
import com.ta.gulimall.product.constant.ProductConstants;
import com.ta.gulimall.product.dao.CategoryDao;
import com.ta.gulimall.product.view.entity.CategoryEntity;
import com.ta.gulimall.product.service.CategoryBrandRelationService;
import com.ta.gulimall.product.service.CategoryService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ta.gulimall.product.view.vo.Catalog2Vo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
    @Autowired
    CategoryBrandRelationService categoryBrandRelationService;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    RedissonClient redissonClient;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> getCategorys() {
        List<CategoryEntity> categoryEntities = categoryListRedis();

        /*所有的一级菜单*/
        List<CategoryEntity> level1Menu = categoryEntities.stream()
                .filter(categoryEntity -> categoryEntity.getParentCid().equals(0L))
                .map(category -> category.setChildren(getchildrens(category, categoryEntities)))
                .sorted(Comparator.comparingInt(CategoryEntity::getSort))
                .collect(Collectors.toList());

        return level1Menu;
    }

    @Override
    public void deleteCategorys(List<Long> catIds) {
        if(ObjectUtil.isEmpty(catIds)){
            return;
        }
        this.removeByIds(catIds);
    }

    @Override
    public List<Long> getClassifyPath(Long catelogId) {
        List<Long> path = new ArrayList<>();
        getParentCid(catelogId, path);
        Collections.reverse(path);
        return path;
    }

    @Override
    @Transactional
    public void updateCategory(CategoryEntity category) {
        updateById(category);
        categoryBrandRelationService.updateCascadeCategory(category);
        redisUtil.del(RedisConstant.PRODUCt_CATEGORY);
    }

    @Override
    public Map<Long, CategoryEntity> mapCategory(List<Long> catalogIds) {
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .in(CategoryEntity::getCatId, catalogIds);

        return list(wrapper).stream().collect(Collectors.toMap(CategoryEntity::getCatId, Function.identity()));
    }

    @Override
    public List<CategoryEntity> getLevel1Categories() {
        List<CategoryEntity> list = categoryListRedis();

        return list.stream().filter(f -> f.getParentCid().equals(ProductConstant.LEVEL1)).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<Catalog2Vo>> getCatalogJson() {
//        List<Long> collect = getLevel1Categories().stream().map(CategoryEntity::getCatId).collect(Collectors.toList());
        List<CategoryEntity> list = categoryListRedis();
        Map<String, List<Catalog2Vo>> map = getLevel1Categories().stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            List<Catalog2Vo> catalog2VoList = list.stream().filter(f -> f.getParentCid().equals(v.getCatId()))
                    .map(m -> new Catalog2Vo()
                            .setCatalog1Id(m.getParentCid().toString())
                            .setId(m.getCatId().toString())
                            .setName(m.getName())
                            .setCatalog3List(list.stream().filter(f -> f.getParentCid().equals(m.getCatId()))
                                    .map(ma -> new Catalog2Vo.Catalog3Vo()
                                            .setCatalog2Id(ma.getParentCid().toString())
                                            .setId(ma.getCatId().toString())
                                            .setName(ma.getName()))
                                    .collect(Collectors.toList())))
                    .collect(Collectors.toList());
            return catalog2VoList;
        }));

        return map;
    }

    @Override
    public List<CategoryEntity> categoryListRedis() {
        List<CategoryEntity> categoryEntities = redisUtil.get(RedisConstant.PRODUCt_CATEGORY, new TypeReference<List<CategoryEntity>>() {
        });
        if(ObjectUtil.isNull(categoryEntities)){
            RLock lock = redissonClient.getLock(RedisConstant.PRODUCt_CATEGORY_QUERY);
            List<CategoryEntity> list;
            try {
                lock.lock();
                List<CategoryEntity> li = redisUtil.get(RedisConstant.PRODUCt_CATEGORY, new TypeReference<List<CategoryEntity>>() {
                });
                if (ObjectUtil.isNotEmpty(li)) {
                    list = li;
                }else{
                    list = list();
                    redisUtil.set(RedisConstant.PRODUCt_CATEGORY, list, ProductConstants.Redis_CategoryKey_Timeout);
                }
            }finally {
                lock.unlock();
            }
            return list;
        }else{
            return categoryEntities;
        }
    }

    private Map<String, List<Catalog2Vo>> getStringListMap(List<Long> level1Id) {
        Map<String, List<Catalog2Vo>> map = list(new LambdaQueryWrapper<CategoryEntity>().in(CategoryEntity::getParentCid, level1Id)).stream()
                .map(m -> new Catalog2Vo()
                        .setCatalog1Id(m.getParentCid().toString())
                        .setId(m.getCatId().toString())
                        .setName(m.getName())).collect(Collectors.toList())
                .stream().collect(Collectors.toMap(Catalog2Vo::getCatalog1Id,
                        v -> Stream.of(v).collect(Collectors.toList()),
                        (v1, v2) -> Stream.concat(v1.stream(), v2.stream()).collect(Collectors.toList())));

        List<Long> level2Id = new ArrayList<>();
        map.forEach((k, v) ->{
            List<Long> collect = v.stream().map(m -> Long.valueOf(m.getId())).collect(Collectors.toList());
            level2Id.addAll(collect);
        });

        Map<String, List<Catalog2Vo.Catalog3Vo>> collect = list(new LambdaQueryWrapper<CategoryEntity>().in(CategoryEntity::getParentCid, level2Id)).stream()
                .map(m -> new Catalog2Vo.Catalog3Vo()
                        .setCatalog2Id(m.getParentCid().toString())
                        .setId(m.getCatId().toString())
                        .setName(m.getName())).collect(Collectors.toList())
                .stream().collect(Collectors.toMap(Catalog2Vo.Catalog3Vo::getCatalog2Id,
                        v -> Stream.of(v).collect(Collectors.toList()),
                        (v1, v2) -> Stream.concat(v1.stream(), v2.stream()).collect(Collectors.toList())));
        return map.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                v -> v.getValue().stream().map(m -> m.setCatalog3List(collect.get(m.getId()))).collect(Collectors.toList())
        ));
    }

    private void getParentCid(Long catelogId, List<Long> path) {
        if (!catelogId.equals(ProductConstant.LEVEL1)) {
            path.add(catelogId);
            CategoryEntity category = getById(catelogId);
            getParentCid(category.getParentCid(), path);
        }
    }


    /**
     * 通过递归查出所有的子菜单
     * @param category 菜单对象
     * @param categoryEntities 所有的菜单
     * @return
     */
    private List<CategoryEntity> getchildrens(CategoryEntity category, List<CategoryEntity> categoryEntities) {
        return categoryEntities.stream()
                .filter(cate -> cate.getParentCid().equals(category.getCatId()))
                .map(cate -> cate.setChildren(getchildrens(cate, categoryEntities)))
                .sorted(Comparator.comparingInt(CategoryEntity::getSort))
                .collect(Collectors.toList());
    }


}